Pass the EntityManager from the transaction callback into each service method as an optional parameter. Child services use manager.getRepository(Entity) when the manager is provided, falling back to their injected repository for standalone calls. This keeps all writes in the same atomic unit without restructuring the service layer.
Pass EntityManager as an optional parameter — services remain callable both inside and outside a transaction.
Never use the injected Repository inside a transaction — it operates on a separate connection.
manager.getRepository(Entity) returns a repository bound to the transactional connection.
This pattern avoids the need for cls-hooked (Continuation Local Storage) which adds runtime complexity.
The orchestrating service owns the transaction boundary; child services are unaware of transaction details.